/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package worldwind.kml.dom;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

/**
 *
 */
public class KMLEventParser {

    public void parseFile(File kmlFile) {
        try {
            XMLInputFactory inputFactory = XMLInputFactory.newInstance();
            InputStream inputStream = new FileInputStream(kmlFile);
            XMLEventReader eventReader =
                    inputFactory.createXMLEventReader(inputStream);

            XMLEvent event = eventReader.nextEvent();

            /**
             * Iterate to the first start element and then break the loop. If
             * the first start element is not kml nothing will happen.
             */
            while (eventReader.hasNext() && !event.isStartElement()) {
                event = eventReader.nextEvent();

                if (event.isStartElement()) {
                    StartElement startElement = event.asStartElement();
                    String localPart = startElement.getName().getLocalPart();

                    if (localPart.equals("kml")) {
                        KMLRoot root = new KMLRoot();
                        parseKMLDocument(root, eventReader);
                        root.saveXML();
                    }
                }
            }

        } catch (FactoryConfigurationError e) {
            System.out.println("FactoryConfigurationError" + e.getMessage());
        } catch (XMLStreamException e) {
            System.out.println("XMLStreamException" + e.getMessage());
        } catch (IOException e) {
            System.out.println("IOException" + e.getMessage());
        }
    }

    private void parseKMLDocument(KMLRoot root, XMLEventReader eventReader) throws XMLStreamException {
        XMLEvent event;
        int events = 0;

        while (eventReader.hasNext()) {
            event = eventReader.nextEvent();

            if (event.isStartElement()) {
                StartElement startElement = event.asStartElement();
                String localPart = startElement.getName().getLocalPart();

                if (localPart.equals("Document")) {
                    KMLDocument document = new KMLDocument();
                    parseDocument(document, eventReader);
                    root.setRootFeature(document);
                }

                if (localPart.equals("Folder")) {
                    KMLFolder folder = new KMLFolder();
                    parseFolder(folder, eventReader);
                    root.setRootFeature(folder);
                }

                if (localPart.equals("Placemark")) {
                    KMLPlacemark placemark = new KMLPlacemark();
                    parsePlacemark(placemark, eventReader);
                    root.setRootFeature(placemark);
                }

                if (localPart.equals("NetworkLink")) {
                    //Do something
                }

                if (localPart.equals("NetworkLinkControl")) {
                    //Do something
                }
                events++;
            }

            if (event.isEndElement()) {
                events--;
            }

            if (events < 0) {
                return;
            }
        }
    }

    private void parseDocument(KMLDocument document, XMLEventReader eventReader) throws XMLStreamException {
        XMLEvent event;
        int events = 0;

        while (eventReader.hasNext()) {
            event = eventReader.nextEvent();

            if (event.isStartElement()) {
                StartElement startElement = event.asStartElement();
                String localPart = startElement.getName().getLocalPart();
                if (localPart.equals("name")) {
                    document.setName(getNodeValue(eventReader));
                }
                if (localPart.equals("visibility")) {
                    document.setVisibile(getBooleanNodeValue(eventReader));
                }
                if (localPart.equals("open")) {
                    document.setOpen(getBooleanNodeValue(eventReader));
                }
                if (localPart.equals("Folder")) {
                    KMLFolder folder = new KMLFolder();
                    parseFolder(folder, eventReader);
                    document.addFeature(folder);
                }
                if (localPart.equals("Placemark")) {
                    KMLPlacemark placemark = new KMLPlacemark();
                    parsePlacemark(placemark, eventReader);
                    document.addFeature(placemark);
                }
                events++;
            }

            if (event.isEndElement()) {
                events--;
            }

            if (events < 0) {
                return;
            }
        }
    }

    private void parseFolder(KMLFolder folder, XMLEventReader eventReader) throws XMLStreamException {
        XMLEvent event;
        int events = 0;

        while (eventReader.hasNext()) {
            event = eventReader.nextEvent();

            if (event.isStartElement()) {
                StartElement startElement = event.asStartElement();
                String localPart = startElement.getName().getLocalPart();
//                System.out.println(localPart + " " + events);
                if (localPart.equals("Folder")) {
                    KMLFolder f = new KMLFolder();
                    parseFolder(f, eventReader);
                    folder.addFeature(f);
                }
                if (localPart.equals("name")) {
                    folder.setName(getNodeValue(eventReader));
                }
                if (localPart.equals("Placemark")) {
                    KMLPlacemark placemark = new KMLPlacemark();
                    parsePlacemark(placemark, eventReader);
                    folder.addFeature(placemark);
                }
                events++;
            }

            if (event.isEndElement()) {
                events--;
            }

            if (events < 0) {
                return;
            }
        }
    }

    private void parsePlacemark(KMLPlacemark placemark, XMLEventReader eventReader) throws XMLStreamException {
        XMLEvent event;
        int events = 0;

        while (eventReader.hasNext()) {
            event = eventReader.nextEvent();

            if (event.isStartElement()) {
                StartElement startElement = event.asStartElement();
                String localPart = startElement.getName().getLocalPart();
//                System.out.println(localPart + " " + events);

                if (localPart.equals("name")) {
                    placemark.setName(getNodeValue(eventReader));
                }
                if (localPart.equals("visibility")) {
                    placemark.setVisibile(getBooleanNodeValue(eventReader));
                }
                if (localPart.equals("open")) {
                    placemark.setOpen(getBooleanNodeValue(eventReader));
                }
                if (localPart.equals("address")) {
                    placemark.setAddress(getNodeValue(eventReader));
                }
                if (localPart.equals("Snippet")) {
                    placemark.setSnippet(getNodeValue(eventReader));
                }
                if (localPart.equals("description")) {
                    placemark.setDescription(getNodeValue(eventReader));
                }
                if (localPart.equals("TimeSpan")) {
                    KMLTimeSpan timeSpan = new KMLTimeSpan();
//                    parseTimeSpan(timeSpan, eventReader);
                    placemark.setTimePrimitive(timeSpan);
                }
                if (localPart.equals("TimeStamp")) {
                    KMLTimeStamp timeStamp = new KMLTimeStamp();
//                    parseTimeStamp(timeSpan, eventReader);
                    placemark.setTimePrimitive(timeStamp);
                }
                if (localPart.equals("styleUrl")) {
                    placemark.setStyleUrl(getNodeValue(eventReader));
                }
                if (localPart.equals("Style")) {
                    KMLStyle style = new KMLStyle();
//                    parseStyle(style, eventReader);
                    placemark.addStyleSelector(style);
                }
                if (localPart.equals("StyleMap")) {
                    KMLStyleMap styleMap = new KMLStyleMap();
//                    parseStyleMap(styleMap, eventReader);
                    placemark.addStyleSelector(styleMap);
                }
                if (localPart.equals("Point")) {
                    KMLPoint point = new KMLPoint();
                    parsePoint(point, eventReader);
                    placemark.setGeometry(point);
                    events--;
                }
                if (localPart.equals("LineString")) {
                    KMLLineString lineString = new KMLLineString();
                    parseLineString(lineString, eventReader);
                    placemark.setGeometry(lineString);
                    events--;
                }
                if (localPart.equals("LinearRing")) {
                    KMLLinearRing linearRing = new KMLLinearRing();
                    parseLinearRing(linearRing, eventReader);
                    placemark.setGeometry(linearRing);
                    events--;
                }
                events++;
            }

            if (event.isEndElement()) {
                events--;
            }

//            System.out.println(events);
//            System.out.println(events);
            if (events < 0) {
                return;
            }
        }
    }

    private void parsePoint(KMLPoint point, XMLEventReader eventReader) throws XMLStreamException {
        XMLEvent event;
        int events = 0;

        while (eventReader.hasNext()) {
            event = eventReader.nextEvent();

            if (event.isStartElement()) {
                StartElement startElement = event.asStartElement();
                String localPart = startElement.getName().getLocalPart();

                if (localPart.equals("coordinates")) {
                    point.setCoordinates(getNodeValue(eventReader));
                }
                events++;
            }

            if (event.isEndElement()) {
                events--;
            }

            if (events < 0) {
                return;
            }
        }
    }

    private void parseLineString(KMLLineString lineString, XMLEventReader eventReader) throws XMLStreamException {
        XMLEvent event;
        int events = 0;

        while (eventReader.hasNext()) {
            event = eventReader.nextEvent();

            if (event.isStartElement()) {
                StartElement startElement = event.asStartElement();
                String localPart = startElement.getName().getLocalPart();

                if (localPart.equals("coordinates")) {
                    lineString.setCoordinates(getNodeValue(eventReader));
                }
                events++;
            }

            if (event.isEndElement()) {
                events--;
            }

            if (events < 0) {
                return;
            }
        }
    }

    private void parseLinearRing(KMLLinearRing linearRing, XMLEventReader eventReader) throws XMLStreamException {
        XMLEvent event;
        int events = 0;

        while (eventReader.hasNext()) {
            event = eventReader.nextEvent();

            if (event.isStartElement()) {
                StartElement startElement = event.asStartElement();
                String localPart = startElement.getName().getLocalPart();

                if (localPart.equals("coordinates")) {
                    linearRing.setCoordinates(getNodeValue(eventReader));
                }
                events++;
            }

            if (event.isEndElement()) {
                events--;
            }

            if (events < 0) {
                return;
            }
        }
    }

    private String getNodeValue(XMLEventReader eventReader) throws XMLStreamException {
        XMLEvent event = eventReader.nextEvent();
        String nodeValue = null;

        if (event.isCharacters()) {
            Characters text = event.asCharacters();
            if (!text.isWhiteSpace()) {
                nodeValue = text.getData();
            }
            if (text.isWhiteSpace()) {
                nodeValue = " ";
            }
        }
        return nodeValue;
    }

    private boolean getBooleanNodeValue(XMLEventReader eventReader) throws XMLStreamException {
        XMLEvent event = eventReader.nextEvent();
        boolean b = false;

        if (event.isCharacters()) {
            Characters text = event.asCharacters();
            if (!text.isWhiteSpace()) {
                int i = Integer.valueOf(text.getData());
                if (i == 1) {
                    b = true;
                }
            }
        }
        return b;
    }

    public static void main(String args[]) {
        if (args.length > 0) {
            String filepath = args[0];
            File file = new File(filepath);
            KMLEventParser parser = new KMLEventParser();
            parser.parseFile(file);
        } else {
            System.out.print("No file found.");
        }

    }
}
